/*
 * Feb/13/2013
 * 
 * Group 2
 * ITCS 3146
 * 
 * Group Project
 * MULTICS Paging Algorithm
 * 
 */


package MULTICS;

import java.util.*;


public class MemorySystem 
{

	
	private Page[] RAM = null;
	
	private SegmentDescriptor[] segmentTable = null;
	private PageFaultHandler pageFaultSystem = null;

	
	// Sets up segment table and page fault system
	MemorySystem() 
	{
	
		//Generates 5-7 Segments
		Random generator = new Random();
		int randNumb = generator.nextInt(3)+5; 
		segmentTable = new SegmentDescriptor[randNumb];

		// Fill the segment table
		for (int i = 0; i < randNumb; i++) 
		{
			segmentTable[i] = new SegmentDescriptor(i);
		}
		
		// Create the page fault system
		pageFaultSystem = new PageFaultHandler();
		
		
	}

	// Loads 6 random pages into memory
	public void PopulateRAM()
	{
		
		RAM  = new Page[6];
		
		Random generator = new Random();
		
		// Number of pages currently in RAM
		int i = 0;
		
		// Ensures 6 pages are added to RAM
		do
		{
			
			// Decides which segment to select pages from
			int randNumb = generator.nextInt(segmentTable.length);
			
			// Returns the page table for the selected segment
			PageDescriptor[] pageTable = segmentTable[randNumb].returnPageTable();
			
			// Decides which page to access
			int randNumb2 = generator.nextInt(pageTable.length);
			
			// Ensures the page is not currently already in RAM
			if(!pageTable[randNumb2].pageStatus())
			{
				// Sets page descriptor's Pres/Abs bit to 1
				pageTable[randNumb2].setPresAbs(true);
				// Sets the page descriptors assigned page frame number in RAM
				segmentTable[randNumb].returnPageTable()[randNumb2].setFrameNumber(i);
				// Creates a page entity in the correct page frame
				RAM[i]= new Page(i,pageTable[randNumb2],segmentTable[randNumb]);
				
				
				// Increase because new page is created
				i++;
				
				// If segment was not already in memory its status is updated
				if(!segmentTable[randNumb].Paged())
				{
					segmentTable[randNumb].updatePageStat(true);
					
				}
					
				
			}
			
			
		}while(i<6);
		
		
	}
	
	
	// Creates a report of current segments and pages
	public String pageSegmentReport() 
	{
		String report = "Segment     Page     Protection     In Memory";
		report = report + "\n**********************************************\n";

		int i = 0;
		for (SegmentDescriptor seg : segmentTable) 
		{
			report = report + "Segment " + i +":      Memory Status - " +seg.Paged() +"\n";

			PageDescriptor[] pageTable = seg.returnPageTable();

			int j = 0;
			for (PageDescriptor page : pageTable) 
			{
				report = report + "            Page " + j + "      "
						+ page.returnProtection()+"           "+ page.pageStatus() + "\n";
				j++;
			}

			report = report + "\n\n";

			i++;

		}

		return report;
	}
	
	
	
	/*
	 *
	 * MULTICS Paging Algorithm
	 * 
	 */
	
	
	// Simulates a memory reference
	// Returning false means a reference failure
	public boolean memoryReference(int segment, int page,int offset, String operation)
	{
		
		
		// Step One
	
		// Finds the specified segment descriptor
		SegmentDescriptor segmentDescrip = stepOne(segment);
		
		// Checks for segment fault
		if(segmentDescrip == null)
		{
			System.out.println("Segment Requested Is Invalid");
			return false;
		}
		else
		{
			System.out.println("Step 1 - Segment Descriptor Located");
			
			//Step Two
			// Assumption: Segment page table is in memory if the segment is currently paged
			
			
			// Get page table for selected segment
			PageDescriptor[] segmentsPageTable = stepTwo(segment,operation);
			
			//If system is trapped end memory reference
			if( segmentsPageTable == null)
				return false;
			
			System.out.println("Step 2 - Segment's Page Table Located");
			
			
			
			
			
			// Step Three
			System.out.println("Step 3 - Physical Address of Page is Found");
			
			// Get physical address of needed page
			String memoryAddress = stepThree(page,segment, segmentsPageTable);
			System.out.println("Memory Address (24-bit) of PageFrame: " + memoryAddress);
			
			
			
			
			// Step Four
			System.out.println("Step 4 - Offset Applied to Page's Physical Address");
			
			// Get offset physical address
			String offsetAddress = stepFour(page,offset,segmentsPageTable);
			System.out.println("Memory Address (24-bit) of PageFrame with Offset Applied: " + offsetAddress);
			
			
			
			// Step 5
			// This step is not needed
			
			System.out.println("Step 5 - Operation is Completed");
			
			return true;
					
			
		}
		
		
	}
	
	
	/*
	 * 
	 * Algorithm Steps
	 * 
	 */
	
	
	// Attempts to find the segment descriptor for the desired segment
	private SegmentDescriptor stepOne(int segmentNumb)
	{
		
		// If a segment that does not exist is requested then a null SegmentDescriptor is returned
		if(segmentNumb < segmentTable.length)
		{
			return segmentTable[segmentNumb];
		}
		else
		{
			return null;
		}
	}
	
	
	
	// Returns a segments page table
	private PageDescriptor[] stepTwo(int segment, String operation)
	{
		//  Check for a segment fault
		// 	Will handle a segment fault like a page fault:
		// 	First page in segment will be paged into RAM
		if(!segmentTable[segment].Paged())
		{
			System.out.println("Segment Fault has Occured");
			pageFaultSystem.pageFault(RAM, segmentTable[segment].returnPageTable()[0], segmentTable[segment],true);
		}
					
		// Protection Check
		// Traps if a protection error occurs
		for(int i=0; i<3; i++)
		{
			if(operation.charAt(i)=='1' && segmentTable[segment].returnProtection().charAt(i)== '0')
			{
				System.out.println("Protection Fault");
				return null;
			}
		}
		
	
		return segmentTable[segment].returnPageTable();
		
	}
	
	
	// Returns physical address of the page
	private String stepThree(int page, int segment, PageDescriptor[] segmentsPageTable)
	{
		
		// Page faults if needed
		// Results in the pageFaultSystem being used
		if(!segmentsPageTable[page].pageStatus())
			pageFaultSystem.pageFault(RAM, segmentsPageTable[page],segmentTable[segment],false);
		
		// Returns Physical Address
		return RAM[segmentsPageTable[page].returnFrameNumber()].returnPhysicalAddress();
	}
	
	// Returns physical address after offset applied
	private String stepFour(int page,int offset, PageDescriptor[] segmentsPageTable)
	{
		return RAM[segmentsPageTable[page].returnFrameNumber()].applyOffset(offset);
	}
	

}
